import utils
import re
import random
import time
import json
import insert_sql
import requests
import threading
from login import OneClickLogin, use_default_login
from urllib3.exceptions import InsecureRequestWarning
# 禁用安全请求警告
requests.urllib3.disable_warnings(InsecureRequestWarning)


class WeChatClient:
    def __init__(self, login_service, url='https://mp.weixin.qq.com'):
        """
        通过moka登录接口获取公众号后台资料
        :param OneClickLogin login_service:
        """
        self.url = url
        self.login = login_service
        self.session = utils.requests_retry_session()
        self.gh_id = None
        self.gh_nickname = None
        self.token = None
        self.index_url = None
        self.index_response = None
        self.violation_list = []

    def log_line(self, msg, gh_nickname=None, gh_id=None):
        line = '[{}]({}) {}'.format(gh_nickname if gh_nickname else self.gh_nickname,
                                    gh_id if gh_id else self.gh_id, msg)
        print(line)
        return line

    def log_err(self, err, logtype='log', gh_nickname=None, gh_id=None):
        utils.log_error(self.log_line(err, gh_nickname, gh_id), logtype)

    def debug_log(self, err, logtype='debug', gh_nickname=None, gh_id=None):
        utils.log_error(self.log_line(err, gh_nickname, gh_id), logtype)
        print(err)

    def detect_account_type(self):
        m = re.search(r'>((订阅|服务)号)</a>', self.index_response, re.MULTILINE)
        if m:
            return m.group(1)

    def enter(self, gh_id, gh_nickname):
        cookies = self.login.get_wx_cookie(gh_id)
        if cookies:
            self.log_line('获取Cookie成功', gh_nickname, gh_id)
            utils.set_cookies(self.session, cookies)
        else:
            self.log_err('一键登录状态异常', gh_nickname=gh_nickname, gh_id=gh_id)
            return False
        try:
            response = self.session.get(url=self.url, verify=False, timeout=15)
            self.index_url = str(response.url)
            try:
                token = re.findall('token=(\\d+)', self.index_url)[0]
            except IndexError:
                self.log_err('微信后台登录失败，检查Cookie是否有效',
                             gh_nickname=gh_nickname, gh_id=gh_id)
                return False
            self.gh_id = gh_id
            self.gh_nickname = gh_nickname
            self.token = token
            self.index_response = response.content.decode('utf-8')
            self.log_line('后台登录成功，Token={}'.format(token))
            return True
        except Exception as e:
            print(e)
            return False

    def save_data(self, data_file, data_date, data):
        with utils.open_write_data_file(self.gh_id, data_file, data_date) as f:
            json.dump(data, f)

    def detect_or_save(self, data_file, data_name, data_date, func, skip_exist=True, verbose=True):
        if skip_exist and utils.has_data_file(self.gh_id, data_file, data_date):
            if verbose:
                self.log_line('已存在{}，跳过'.format(data_name))
        else:
            if verbose:
                self.log_line('开始获取{}'.format(data_name))
            data = func(data_date)
            if data:
                if verbose:
                    self.log_line('获取{}成功'.format(data_name))
                self.save_data(data_file, data_date, data)
            else:
                self.log_err('获取{}失败,请重试,或单独爬取'.format(data_name))

    def save_basic_info(self, account, date):
        self.save_data('basic.json', date, account)

    def save_subscriber_info(self, date):
        resp = self.session.get(
            '{0}/misc/useranalysis?&begin_date={1}&end_date={1}&source=99999999&token={2}&lang=zh_CN&f=json&'
            'ajax=1&random={3}'.format(self.url, utils.get_yesterday(), self.token, random.random()), verify=False,
            timeout=15)
        try:
            data = resp.json()
            return data['category_list'][0]['list'][0] if data['category_list'][0]['list'][0] else None
        except (KeyError, ValueError, TypeError, IndexError):
            self.debug_log(resp.content)

    def save_subscriber_attr(self, date):
        resp = self.session.get(
            '{0}/misc/useranalysis?action=attr&begin_date={1}&end_date={1}&token={2}&lang=zh_CN&f=json&ajax=1'.format(
                self.url, date, self.token), verify=False, timeout=15)
        try:
            data = resp.json()
            if data['load_done'] and data['user_portrait']['list']:
                return data['user_portrait']['list'][0]
            else:
                self.log_err("微信后台没有数据")
                return None
        except (KeyError, ValueError, TypeError, IndexError):
            self.debug_log(resp.content)

    def save_violation_info(self):
        self.violation_list = []
        resp = self.session.get('{0}/cgi-bin/sysnotify?token={1}&lang=zh_CN&f=json&ajax=1&random={2}&begin=0&'
                                'count=999999&status=0'.format(self.url, self.token, random.random()), verify=False,
                                timeout=15)
        dates = set()
        try:
            data = resp.json()
            for item in data['List']:
                if 'Title' in item:
                    if re.search('(?:违规|责令处理|屏蔽)', item['Title']):
                        self.violation_list.append(item)
                        dates.add(time.strftime(
                            '%Y-%m-%d', time.localtime(item['UpdateTime'])))
            for date in dates:
                self.detect_or_save('violation-list.json', '违规处理', date, self.violation_filter_date,
                                    skip_exist=date == utils.get_today(), verbose=False)
        except (KeyError, ValueError, TypeError):
            self.log_err('获取违规失败')
            self.debug_log(resp.content.decode('utf-8'))


    def violation_filter_date(self, date):
        violations = []
        for item in self.violation_list:
            update_time = time.strftime(
                '%Y-%m-%d', time.localtime(item['UpdateTime']))
            if date == update_time:
                violations.append({
                    'Id': item['Id'],
                    'UpdateTime': update_time,
                    'Content': item['Content'],
                    'Title': item['Title']
                })
        return violations

    def save_messages_info(self, date):
        resp = self.session.get(
            '{0}/misc/appmsganalysis?action=report&type=daily&begin_date={1}&end_date={1}&token={2}'
            '&lang=zh_CN&f=json&ajax=1&random={3}'.format(self.url, date, self.token, random.random()), verify=False,
            timeout=15)
        try:
            data = resp.json()
            return {'item': data['item'], 'share': data['share_item']} if data['load_done'] else None
        except (KeyError, ValueError, TypeError):
            self.debug_log(resp.content)

    def get_nickname_when_not(self):
        resp = self.session.get(
            '{0}/cgi-bin/settingpage?t=setting/index&action=index&token={1}&lang=zh_CN&f=json&ajax=1&random={2}'
            .format(self.url, self.token, random.random()), verify=False, timeout=15)
        nickname = ''
        wx_id = ''
        gs = ''
        try:
            data = resp.json()
            nickname = data['setting_info']['nickname']['nickname']
            wx_id = data['setting_info']['username']
            gs = data['setting_info']['contractor_info']['name']
        except (KeyError, ValueError, TypeError):
            self.debug_log(resp.content)
        return nickname, wx_id, gs


class Threading_thread(threading.Thread):
    def __init__(self, login_service, account, data_date=None):
        threading.Thread.__init__(self)
        self.login_service = login_service
        self.account = account
        self.data_date = data_date

    def run(self):
        if not self.data_date:
            self.data_date = utils.get_today()
        data_date = utils.get_yesterday()
        srv = WeChatClient(self.login_service)
        if not srv.enter(self.account['gh_id'], self.account['nickname']):
            return
        account_type = srv.detect_account_type()
        if account_type:
            srv.log_line(account_type)
            self.account['type'] = account_type
        self.account['nickname'], self.account['wx_id'], self.account['gs'] = srv.get_nickname_when_not()
        srv.save_basic_info(self.account, self.data_date)
        srv.save_violation_info()
        srv.detect_or_save('user-counter.json', '关注统计数', data_date,
                           srv.save_subscriber_info)
        srv.detect_or_save('user-attr.json', '用户属性统计数', data_date,
                           srv.save_subscriber_attr)
        sql_info_violation(srv, self.data_date, self.account)
        sql_sealing_violation_info(srv, self.data_date, self.account)


def sql_info_violation(srv, data_date, account):
    # 数据表格 数据入库
    try:
        u = srv.save_subscriber_info(data_date)
        accumulative_attention = u.get('cumulate_user')
        v = insert_sql.get_violation_list(data_date, account['gh_id'])
        if v:
            try:
                violation_list_by_title = {}
                for value in v:
                    violation_cause, violation_title, violation_result = insert_sql.get_violation_info(value)
                    if violation_title in violation_list_by_title:
                        if violation_result:
                            violation_list_by_title[violation_title] = [
                                violation_cause, violation_list_by_title[violation_title][1] + violation_result]
                        else:
                            violation_list_by_title[violation_title] = [
                                violation_list_by_title[violation_title][0],
                                violation_list_by_title[violation_title][1] + violation_result]
                    else:
                        violation_list_by_title[violation_title] = [
                            violation_cause, violation_result]

                for vio_title, vio_info in violation_list_by_title.items():
                    if vio_info[1]:
                        whether_or_not_to_ban = '是'
                        insert_sql.violation_data_table(data_date, account, vio_info[0],
                                                        vio_title, vio_info[1], whether_or_not_to_ban,
                                                        accumulative_attention)
                    else:
                        pass
            except Exception as e:
                print(e)
    except Exception as e:
        print(e)

def sql_sealing_violation_info(srv, data_date, account):
    # 数据表格 功能屏蔽 数据入库
    custom_menu = automatic_recovery = articles_are_searched = group_sending_text = traffic_master = ""
    friendship_circle = template_message = customer_service_news = callback = read_the_original_text = ""
    searched = concerned_about = advertiser = mass_of_news = open_web = ""
    u = srv.save_subscriber_info(data_date)
    accumulative_attention = u.get('cumulate_user')
    v = insert_sql.get_violation_list(data_date, account['gh_id'])
    if v:
        try:
            for value in v:
                violation_result = insert_sql.get_violation_sealing_info(value)
                if violation_result:
                    for y in range(len(violation_result)):
                        forbidden_features = violation_result[y][0]
                        feature_date = violation_result[y][1]
                        if forbidden_features == "自定义菜单":
                            custom_menu = feature_date
                        elif forbidden_features == "自动回复":
                            automatic_recovery = feature_date
                        elif forbidden_features == "文章被搜索":
                            articles_are_searched = feature_date
                        elif forbidden_features == "群发文本":
                            group_sending_text = feature_date
                        elif forbidden_features == "文章分享到朋友圈可见":
                            friendship_circle = feature_date
                        elif forbidden_features == "模版消息":
                            template_message = feature_date
                        elif forbidden_features == "客服消息":
                            customer_service_news = feature_date
                        elif forbidden_features == "callback":
                            callback = feature_date
                        elif forbidden_features == "阅读原文":
                            read_the_original_text = feature_date
                        elif forbidden_features == "流量主":
                            traffic_master = feature_date
                        elif forbidden_features == "被搜索":
                            searched = feature_date
                        elif forbidden_features == "被关注":
                            concerned_about = feature_date
                        elif forbidden_features == "广告主":
                            advertiser = feature_date
                        elif forbidden_features == "消息群发":
                            mass_of_news = feature_date
                        elif forbidden_features == "从公众号打开网页":
                            open_web = feature_date
                    insert_sql.violation_sealing_data_table(data_date, account, custom_menu,
                                                            automatic_recovery, articles_are_searched,
                                                            group_sending_text, accumulative_attention,
                                                            friendship_circle, template_message,
                                                            customer_service_news, callback, read_the_original_text,
                                                            traffic_master, searched, concerned_about,
                                                            advertiser, mass_of_news, open_web)
        except Exception as e:
            print(e)


def test_collect(thread=8):
    library = utils.default_library()
    account_chunks = library.get_account_chunks(thread)
    login_srv = use_default_login()
    pool = []
    for gh_id_list in account_chunks:
        for gh_id in gh_id_list:
            account = library.get_account_data(gh_id)
            thread = Threading_thread(login_srv, account)
            thread.start()
            pool.append(thread)
        for thread in pool:
            thread.join()
